USBメモリ ルートディレクトリ
 ファイル検索


(→プロジェクトファイル(Harmony Ver.2.04版 ) ダウンロード


  USBメモリのルートディレクトリにあるファイルの数 及びファイル名を検索する例を紹介します。
 




<仕様>
 ・USBメモリのルートディレクトリにあるファイルの総数をカウントしてキャラクタ液晶に表示する。
 ・USBメモリのルートディレクトリにあるファイルの名称をすべて表示する。
 ・ファイル名の表示はキャラクタ液晶 及び TeraTerm(PCとの接続はUART)とする。
 ・キャラクタ液晶への ファイル数 及び各ファイル名表示は 2~3sec の間隔をあけて順次1項目づつ表示のこと 
 ・ファイルは押しボタンスイッチ(SW2)をクリックすることにより 開いているファイルを閉じて次のファイルをオープンするかたちで
  スキップできること。 
   またスキップ後、新たにオープンしたファイル名を 閉じたファイル名の代わりにキャラクタ液晶に表示のこと。
 ・また、他の押しボタンスイッチ(SW1)をクリックすることにより ファイルを オープン/クローズできること。
   尚、ファイルがオープンしたらLEDを点灯し、クローズしたら消灯すること。



<回路図>( → PDFファイル)


<外観> PIC32MZ評価ボード(→購入方法)を使った実験品の外観です。




<動作結果>(→ 動画 : 1080pのHD動画を見ることができます。)

 キャラクタ液晶  
   項目 画面   備考
ルートディレクトリにある
ファイル総数
 
➁  ファイル名表示(例)  8.3形式ファイル名 
ロングファイル名 

Tera Term画面  
ルートディレクトリにある
全ファイル一覧





<解説>記載してある内容は要点だけです。 詳細はプロジェクトファイルを精読願います。
         (以下は、Harmony v2.04 をもとに作成しています。最新のバージョンは異なることがあるかもしれませんので注意してください。)

 ■MHCの設定
  ■Options

項目  ①Configuration設定
Device & Project Configuration
  > PIC32MZ2048 Device Configuration 
②Heap Size設定
Device & Project Configuration
 >Project Configuration >XC32(Global Opton)
 >XC32-ld >General
③USB Library設定
Harmony Framework Configuration
 > USB Library
MHC     
備考 デフォルトからの変更要領:
 FPLLIDIV: DIV3
 FPLLICLK: PLL_POSC
 POSCMOD: EC

システムクロック周波数: 200MHz
外部
 主発振器: 24MHz

#pragma config FNOSC = SPLL
#pragma config POSCMOD = EC
#pragma config FPLLIDIV = DIV_3
#pragma config FPLLICLK = PLL_POSC
#pragma config FPLLMULT = MUL_50
#pragma config FPLLODIV = DIV_2
デフォルトからの変更要領:
 Heap Size(byte): 512
デフォルトからの変更要領:
 □USE USB Stack? チェック追加
 □USB Host チェック追加
 □USE MSD Host Client Driver チェック追加
項目 ④File System
Harmony Framework Configuration
 >System Services  
⑤USART設定
Harmony Framework Configuration > Drivers
 > USART
⑥Timer System Service
Harmony Framework Configuration
 >System Services  
MHC
備考 デフォルトからの変更要領:
 □USE File System Services? チェック追加
 □USE File System Auto Mount Feature?
                   チェック追加
 Mdedia Type: SYS_FS_MEDIA_TYPE_MSD

(デフォルトはSYS_FS_MEDIA_TYPE_SD_CARDなので注意)
デフォルトからの変更要領:
 Use USART Driver? チェックを入れる
 USART Module ID: USART_ID_6
デフォルトからの変更要領:
 なし
(本項のシステムタイマはデフォルトでチェックが入っている。)
項目 ⑦Timer
Harmony Framework Configuration > Drivers
 > Timer
   
MHC    
備考 デフォルトからの変更要領:
 なし
(本項のタイマはシステムタイマ用のタイマでデフォルトでチェックが入っている。) 
   



■ Pin Settins

項目  ⑧ポート設定
MHC  
備考 デフォルトからの変更要領:
 RG15/Function: GPIO_OUT
        (LED用出力ポート設定)
 RC2/Function: U6TX
        (UART6のTX設定)
 RG9/Function: U6RX
        (UART6のRX設定) 
 RB1/Function: GPIO_IN、
          PULL UP(抵抗)チェック追加
        (SW2用入力ポート設定) 
 RB0/Function: GPIO_IN、
          PULL UP(抵抗)チェック追加
        (SW1用入力ポート設定) 


■キャラクタ液晶表示ライブラリの追加要領
  キャラクタ液晶表示のライブラリ 1lcd_lib_XC32.h と 1lcd_lib_XC32.cを main.cと同じフォルダにコピーして、プロジェクトに追加します。
                                              (使い方 → キャラクタ液晶表示方法 参照
  プロジェクトへの追加は以下のようにします。


■app.h に青字部分を追加します。

➀ 取り扱うファイル数の最大を80個に設定します。
  #define DISK_MAX_FILES 80


➁ファイルを最大64バイトのフルパス付ファイルで扱います。 このフルパスのファイルと Harmonyのファイルが標準で持っている
 ファイルの特性を保持しているSYS_FS_FSTAT構造体をメンバーにもつDISK_FILE_NODE構造体を宣言します。 プログラムの
 なかではこのDISK_FILE_NODE構造体のインスタンスをファイルとして扱います。
   typedef struct{
    SYS_FS_FSTAT fstat;
    char path[64];
  }DISK_FILE_NODE;

  typedef struct{
    char path[64]; // at most 64 bytes long
  }DISK_FILE_PATH;



以下、 app.h



■ app,c に青字部を追加します。

➀インクルードファイルを追加します。stdio.hがないと、sprintf( )がコンパイラのバージョンによってコンパイルで警告となることが
  あります。 
  #include "stdio.h"



➁所要の変数、ファイルハンドル、構造体のインスタンスなどの宣言 及び定義を行います。
  int delay_Clock = 200000000; //システムクロック:200MHz
  char Buf[32];
  char Buf2[32]; //液晶上段、下段のバッファを分ける //下段が16文字を超えると上段の表示に影響がある
  int BtnEvent[2];

  int totalFiles;
  int ysPlay;

  SYS_FS_HANDLE ys_fileHandle;
  SYS_FS_HANDLE ys_dirHandle;
  SYS_FS_FSTAT ys_dirstat; //ファイル・ディレクトリ構造体

  static DISK_FILE_NODE rootNode;
  static DISK_FILE_PATH FilesTable[DISK_MAX_FILES];
  char str_FileName[64]; //ファイル名
  int Num_File = 1;



③USBの接続状態に係る列挙名、USBメモリ読み込み状態に係る列挙名、ボタンスイッチの状態に係る列挙名などを
  定義します。
  enum{ //USBの状態に係る列挙名
      USB_STATE_OPEN_HOST_LAYER,
      USB_STATE_WAIT_FOR_HOST_ENABLE,
      USB_STATE_DEVICE_CONNECTED,
      USB_STATE_WAIT_FOR_DEVICE_ATTACH,
      USB_STATE_UNMOUNT_DISK
     }UsbState;

  enum{ //USBディスクの状態に係る列挙名
      DISK_STATE_INIT,
      DISK_STATE_SCAN_FINISHED,
      DISK_STATE_OPEN_FIRST_FILE,
      DISK_STATE_SCANNING,
      DISK_STATE_RUNNING,
      DISK_STATE_REMOVED,
      DISK_STATE_NO_FILES,
     }DiskState;


  enum {
      APP_OPE_PAUSE_REPLAY = 1,
      APP_OPE_SKIP,
     }AppBtn;

  enum
   { 
    PLAYER_CMD_STOP,
    PLAYER_CMD_PLAY,
    PLAYER_CMD_NEXT_FILE,
   }PlayerCommand;


④関数のプロトタイプ宣言をおこないます。
  //関数のプロトタイプ宣言
  void DISK_NextTrack(void);
  void DISK_CloseTruck (SYS_FS_HANDLE fileHandle);
  void DISK_OpenTrack ( const char *fname );
  bool ys_FileType83(char *myFileName);
  void DISK_TraverseAllFiles(DISK_FILE_NODE node);
  void APP_SYSFSEventHandler(SYS_FS_EVENT event, void * eventData, uintptr_t context);
  USB_HOST_EVENT_RESPONSE APP_USBHostEventHandler (USB_HOST_EVENT event, void * eventData, uintptr_t context);
  void USB_Connection_Tasks(void);
  void DISK_Tasks(void);
  void APP_PlayerTask(void);
  bool APP_PlayerCommand (int cmd);
  void APP_OnButtonEvent(uint8_t button, bool bButtonClosed, int32_t repeatCount);
  void APP_BtnTask(void);


⑤delay_us( )、delay_ms( ) という NOPを使った 1μsec 、1msec単位の遅延関数をつくります。HarmonyではSYS_Tasks ( )の中で
  長い時間の遅延をつかうとその遅延時間だけタスクがそこで止まってしまいますので注意して使う必要があります。
  void delay_us(volatile unsigned int usec) //1μsec遅延
  {
    volatile int count;

    count = (int)(delay_Clock/20000000)*usec;

  ……
  ……


⑥UARTでTeraTermに文字を送信する関数です。
  void Write_Byte(char chr) //1バイト送信関数
  {
    PLIB_USART_TransmitterByteSend(USART_ID_6, chr); //送信バッファーに1バイト書込み・送信
    // U6TXREG = chr; ////送信バッファーに1バイト書込み・送信

    while (!PLIB_USART_TransmitterIsEmpty(USART_ID_6)); //送信バッファーが空になるまで待つ

  ……
  ……


⑦ファイルテーブルFilesTable[ ]の管理番号をインクリメントして次のファイルをオープンする関数です
  void DISK_NextTrack(void) //次のファイル曲へ
  {
    Num_File++;
    if(Num_File > (totalFiles -1))Num_File = 1;
    DISK_OpenTrack(FilesTable[Num_File].path); //ファイルオープン
  }


⑧ファイルをクローズして、LEDを消灯する関数です。
  void DISK_CloseTruck (SYS_FS_HANDLE fileHandle)
  {
    SYS_FS_FileClose ( fileHandle );
    ysPlay = 0;
    LATGbits.LATG15 = 0; //LED1 消灯
  }


⑨ファイルをオープンして LEDを点灯後、ファイル名を取得してキャラクタ液晶に表示しています。
  void DISK_OpenTrack ( const char *fname )
  {
    char tempBuf[16];

    ys_fileHandle = SYS_FS_FileOpen(fname, (SYS_FS_FILE_OPEN_READ_PLUS)); //読み込みモードで、ファイルオープン、
    ysPlay = 1;
    LATGbits.LATG15 = 1; //LED1 点灯



    SYS_FS_FileNameGet(ys_fileHandle, str_FileName, 32 ); //ファイル名取得

    lcd_cmd(0x80); //1目の先頭へ
    sprintf(Buf,"%s ", str_FileName);//
    lcd_str(Buf); //液晶表示
    lcd_cmd(0xC0); //2行目の先頭へ

    sprintf(Buf,"%s ", str_FileName + 16);//str_FileName のポインタを16バイト進める
    lcd_str(Buf); // 開始メッセージ1行目表示

  }



⑩ファイル名からファイルが8.3形式のファイルか ロングファイル名のファイルかを判別する関数です。
 Harmonyの場合 ロングファイル名のファイル名を8.3形式で表示すると先頭から7番目が ~ (0x7E、チルダ)になります。
 このことを利用して判別しています。
  bool ys_FileType83(char *myFileName) //8.3形式ファイルチェック //8.3形式ならtrue
  {
    bool Flag;
    
    if(myFileName[6] != 0x7E) Flag = true; //~が含まれない → 8.3形式ファイル名
    else Flag = false; //~が含まれる → ロングファイル名 //例:MOONRI~1.mp3

    return Flag;
  }


 ⑪DISK_TraverseAllFiles( )関数でこのnod(ディレクトリ)内のファイルを検索して、フルパス付のファイルとしてファイルテーブルFilesTable[ ]に
  書き込まれます。 SYS_FS_DirOpen( )関数でディレクトリをオープンされます。SYS_FS_DirRead(ys_dirHandle,&ys_dirstat); でファイルや
  サブディレクトリが1つづつ、順次検索されるので、検索されたファイルやディレクトリのステータスをys_dirstat構造体から読み出します。
   ys_dirstat.lfname[0] 及び(ys_dirstat.fname[0] が共に\0(NULL)となった場合すべてが読みだされた場合です。ファイル名の呼び出し方が
  異なるので 8.3形式とロングファイル名の場合区別してファイルテーブルに書き込みます。 区別しないですべて8.3形式でテーブルに書き込むと 
  ロングファイル名の7番目が ~ (0x7E、チルダ)になってしまいます。
  (totalFiles)++; インクリメントをおこないファイルの数をカウントします。          
  

void DISK_TraverseAllFiles(DISK_FILE_NODE node) //全ファイル詳細調査関数 { SYS_FS_RESULT ret; bool Flag83; char longFileName[300]; char *lfname = node.path; ys_dirHandle = SYS_FS_DirOpen(lfname); //lfname: フルパスのディレクトリ名 //ルートディレクトリは0 //このdo ループで トータルのファイル数をカウントする。 do { if(totalFiles < DISK_MAX_FILES) //DISK_MAX_FILES = 80 { ys_dirstat.lfname = longFileName; ys_dirstat.lfsize = 300; ret = SYS_FS_DirRead(ys_dirHandle,&ys_dirstat); //ディレクトリ・ファイルのステータスを読み込む //読み込度に 順次異なるディレクトリorファイルを読み込む //諸元は構造体SYS_FS_FSTATのオブジェクトys_dirstatによる if ((ys_dirstat.lfname[0] == '\0') && (ys_dirstat.fname[0] == '\0')) //ディレクトリの終端に達した場合、ここで抜ける { break; } if(ret!= SYS_FS_RES_FAILURE) //読み込み成功なら { if(ys_dirstat.fattrib != SYS_FS_ATTR_DIR) // 属性がディレクトリでない場合 //SYS_FS_ATTR_DIR: ディレクトリ { Flag83 = ys_FileType83(ys_dirstat.fname); //8.3形式ファイルの場合 if(Flag83 == true) { strcpy(FilesTable[totalFiles].path, lfname); //ファイル名fnameを FilesTable[totalFiles].pathにコピー strcat(FilesTable[totalFiles].path, ys_dirstat.fname); (totalFiles)++; } //ロングファイル名のファイルの場合 else if(Flag83 != true) { strcpy(FilesTable[totalFiles].path, lfname); //ファイル名fnameを FilesTable[totalFiles].pathにコピー strcat(FilesTable[totalFiles].path, ys_dirstat.lfname); (totalFiles)++; } } } } }while(ret==SYS_FS_RES_SUCCESS); SYS_FS_DirClose(ys_dirHandle); }

⑫APP_SYSFSEventHandler( )とAPP_USBHostEventHandler( )はUSBマウントに係るコールバック関数で HaarmonyではUSB接続の状態を
 チェックする際に必ず使用されます。 USB接続が完了するとSYS_FS_EVENT eventがSYS_FS_EVENT_MOUNTになります。
  USB_Connection_Tasks();は 無限循環タスクのAPP_Tasks ( )から常に呼ばれるようにしておき、USBディスクがタスク実行中に
 抜かれた場合もプログラムがハングしないようにしておきます。
 

        

 
void APP_SYSFSEventHandler(SYS_FS_EVENT event, void * eventData, uintptr_t context) //USBマウントに係るコールバック関数 { switch(event) { case SYS_FS_EVENT_MOUNT: //マウント完了の場合 UsbState = USB_STATE_DEVICE_CONNECTED; //USBメモリ接続完了 break; case SYS_FS_EVENT_UNMOUNT: //未マウントの場合 UsbState = USB_STATE_UNMOUNT_DISK; UsbState = USB_STATE_WAIT_FOR_DEVICE_ATTACH; ys_fileHandle = SYS_FS_HANDLE_INVALID; totalFiles = 0; break; default: break; } } USB_HOST_EVENT_RESPONSE APP_USBHostEventHandler (USB_HOST_EVENT event, void * eventData, uintptr_t context) { switch(event) { case USB_HOST_EVENT_DEVICE_UNSUPPORTED: break; default: break; } return (USB_HOST_EVENT_RESPONSE)USB_HOST_EVENT_RESPONSE_NONE; } void USB_Connection_Tasks(void) //USB接続状態 { switch(UsbState) { case USB_STATE_OPEN_HOST_LAYER: /* Set the event handler and enable the bus */ SYS_FS_EventHandlerSet(APP_SYSFSEventHandler, (uintptr_t)NULL); //コールバック関数APP_SYSFSEventHandler()セット USB_HOST_EventHandlerSet(APP_USBHostEventHandler, 0); //コールバック関数APP_USBHostEventHandler()セット // enable, open, bus 0 USB_HOST_RESULT ret = USB_HOST_BusEnable(0); if(ret == USB_HOST_RESULT_FALSE) return; UsbState = USB_STATE_WAIT_FOR_HOST_ENABLE; break; case USB_STATE_WAIT_FOR_HOST_ENABLE: //ホストの準備完了待ち /* Check if the host operation has been enabled */ if(USB_HOST_BusIsEnabled(0)) { /* This means host operation is enabled. We can * move on to the next state */ UsbState = USB_STATE_WAIT_FOR_DEVICE_ATTACH; } break; case USB_STATE_WAIT_FOR_DEVICE_ATTACH: //デバイスの接続待ち /* Wait for device attach. The state machine will move * to the next state when the attach event * is received. */ break; default: break; } }


(13)  DISK_Tasks( )関数は無限循環タスクのAPP_Tasks ( )から常に呼ばれています。 USBの接続状態を監視して USBディスクが
   挿入されたらUSBの初期化などをおこないます。
    PICが起動したり、USBディスクが挿入された場合 DISK_TraverseAllFiles( )関数で ルートディレクトリ内のファイルを検索します。
   検索した結果として、ファイルの総数 やファイルテーブルFilesTable[ ]から読み出したファイル名をキャラクタ液晶に表示します。

          

   
void DISK_Tasks(void) { int i; USB_Connection_Tasks(); if(UsbState == USB_STATE_DEVICE_CONNECTED) //USBデバイス接続状態 { //ディスクが抜かれていた場合 → ディスクをスキャンしてリスト作成 if (DiskState == DISK_STATE_REMOVED ) //ファイルなし //ファイルシステムアンマウント //ディスク抜き出し状態 { DiskState = DISK_STATE_INIT; //ディスクをスキャンしてリスト作成 } } else // { if ( DiskState != DISK_STATE_REMOVED ) //ディスク抜き出し状態ではない状態 { DiskState = DISK_STATE_REMOVED; } } switch (DiskState) { //PIC起動後 最初に呼ばれる  --> ディスクの全スキャン (ファイルの フルパス付きファイル名リストテーブルを作成する) case DISK_STATE_INIT: DiskState = DISK_STATE_SCANNING; break; case DISK_STATE_SCANNING: //スキャン実行 DISK_TraverseAllFiles(rootNode); //ルートディレクトリの全ファイルを調査する if(totalFiles == 0) { // No File DiskState = DISK_STATE_NO_FILES; } else { DiskState = DISK_STATE_SCAN_FINISHED; lcd_cmd(0x80); //1目の先頭へ sprintf(Buf,"TotalFile=%d ", (totalFiles - 1)); //FilesTable[0].pathは、System Volume Information lcd_str(Buf); // 開始メッセージ1行目表示 lcd_cmd(0xC0); //2行目の先頭へ sprintf(Buf," "); // lcd_str(Buf); // 開始メッセージ1行目表示 delay_ms(3000); //全ファイル表示 for キャラクタ液晶 & UART for (i = 1; i < totalFiles; i++ ) //FilesTable[0].pathはSystem Volume Information { lcd_cmd(0x80); //1目の先頭へ sprintf(Buf,"%s ", FilesTable[i].path); // lcd_str(Buf); // 開始メッセージ1行目表示 WriteString(Buf); //UARTへ送信 WriteString("\r\n"); //改行 lcd_cmd(0xC0); //2行目の先頭へ sprintf(Buf2,"%s ", FilesTable[i].path + 16); //FilesTable[i]のポインタを16バイト進める lcd_str(Buf2); // 開始メッセージ1行目表示 delay_ms(2000); } } break; case DISK_STATE_SCAN_FINISHED: //スキャン終了 DiskState = DISK_STATE_OPEN_FIRST_FILE; break; case DISK_STATE_OPEN_FIRST_FILE: //最初に呼ばれるファイル Num_File = 1; //ファイルテーブル No = 1 を設定 //No = 0 にはファイルではないもの(System Volume Information)が入っている DISK_OpenTrack(FilesTable[Num_File].path); //ファイルオープン DiskState = DISK_STATE_RUNNING; break; case DISK_STATE_RUNNING: //ファイルを繰り返し実行 break; default: break; } }



(14) ボタンスイッチからの信号により ファイルオープン、クローズ、及びスキップする操作に係る関数です。

  bool APP_PlayerCommand (int cmd)
  {
    switch (cmd)
    {
      case PLAYER_CMD_STOP:
         DISK_CloseTruck(ys_fileHandle);
      break;
  ......
  ......



以下、 app.c



■ system_interrupt.c に青字部分を追加します。

➀ ファイルオープン、クローズ、スキップを操作するボタンスイッチのイベントフラグの変数です。
  extern int BtnEvent[2];


➁ 上記ボタンスイッチがクリックされたことを検出しています。
  //Button1 RB0
  if(PORTBbits.RB0 == 0) BtnEvent[0] = 1; //入力変化検出 //ファイル open/close

  //Button2 RB1
  else if(PORTBbits.RB1 == 0) BtnEvent[1] = 1; //入力変化検出 //次のファイル




以下、 system_interrupt.c